#ifndef THREAD_POOL_H
#define THREAD_POOL_H
#include <atomic>
#include <condition_variable>
#include <functional>
#include <future>
#include <iostream>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <vector>
class ThreadPool {
 public:
  using Task = std::function<void()>;
  ThreadPool(int thread_num = std::thread::hardware_concurrency())
      : task_que_(),
        thread_vec_(),
        mtx_(),
        empty_cond_(),
        full_cond_(),
        task_num_limit_(1024),
        stop_(false) {
    std::cout << thread_num << std::endl;
    for (int i = 0; i < thread_num; i++) {
      thread_vec_.emplace_back(std::thread(&ThreadPool::ThreadFunc, this));
    }
    // TODO:封装std::thread，控制线程池启动时机'
  }
  ~ThreadPool() {
    stop_ = true;
    empty_cond_.notify_all();
    for (auto& t : thread_vec_) t.join();
  }
  template <typename Func, typename... Args>
  decltype(auto) AddTask(Func&& func, Args&&... args);
  void SetTaskNumLimit(int n) { task_num_limit_ = n; }
  int GetTaskNumLimit() const { return task_num_limit_; }

 private:
  void ThreadFunc();

 private:
  std::queue<Task> task_que_;
  std::vector<std::thread> thread_vec_;
  std::mutex mtx_;
  std::condition_variable empty_cond_;
  std::condition_variable full_cond_;
  std::atomic<int> task_num_limit_;
  std::atomic<bool> stop_;
};

// 消费者
void ThreadPool::ThreadFunc() {
  Task task;
  while (1) {
    {
      std::unique_lock<std::mutex> ul(mtx_);
      while (!stop_ && task_que_.size() == 0) empty_cond_.wait(ul);
      // 当线程池析构时，任务做完了再返回
      if (task_que_.empty() && stop_) return;
      task = std::move(task_que_.front());
      task_que_.pop();
      full_cond_.notify_one();
    }
    task();
  }
}
// 生产者提交任务使用的接口
// 可变参数模板+尾置返回类型推导
template <typename Func, typename... Args>
decltype(auto) ThreadPool::AddTask(Func&& func, Args&&... args) {
  // 编译期推出返回类型
  using ReturnType = std::result_of_t<Func(Args...)>;
  auto task = std::make_shared<std::packaged_task<ReturnType()>>(
      std::bind(std::forward<Func>(func), std::forward<Args>(args)...));
  // 拿到这个future对象，这是要返回给生产者线程供其取结果的
  // 这也是为什么要使用packaged_task的原因，它也是一个可调用对象，但是它可以返回一个future对象
  std::future<ReturnType> res = task->get_future();
  {
    std::unique_lock<std::mutex> ul(mtx_);
    while (!stop_ && task_que_.size() == task_num_limit_) full_cond_.wait(ul);
    // TODO:设置提交超时时间
    if (stop_) {
      std::cerr << "线程池已停止，不能再提交任务!" << std::endl;
      return res;  // TODO: future status？
    }
    // 对于不同返回类型的任务函数，如何将其统一放入一个容器？包装一层!
    task_que_.emplace([task]() { (*task)(); });
  }
  empty_cond_.notify_one();
  return res;
}
// TODO:支持fixed 和 cached两种模式

#endif  // THREAD_POOL_H